﻿using System;
using System.Collections.Generic;
using Excel = Microsoft.Office.Interop.Excel;

namespace QAToolsAddin {
    static class Frequency {

        public class ContinuousFrequencies {

            internal struct Item {

                public double? LowerIncluded;
                public double? UpperExcluded;
                public int Count;

                public string Name {
                    get {
                        return (LowerIncluded == null ? "[-∞" : "[" + LowerIncluded.ToString())
                            + "  " + (UpperExcluded == null ? "+∞]" : UpperExcluded.ToString() + '[');
                    }
                }
            }

            public readonly string[] Names;
            public readonly int[] Values;
            public readonly double Average;
            public readonly double StandardDeviation;
            public readonly double Min;
            public readonly double Max;

            public ContinuousFrequencies(Item[] classes, double min, double max, double average, double standardDeviation) {
                Names = Utils.ListToArray<Item, string>(classes, delegate(Item item) { return item.Name; });
                Values = Utils.ListToArray<Item, int>(classes, delegate(Item item) { return item.Count; });
                Min = min;
                Max = max;
                Average = average;
                StandardDeviation = standardDeviation;
            }
        }

        public static ContinuousFrequencies ComputeContinuousFrequencies(Excel.Range rgsel, int nbClasses, int precision) {
            var cellOp = new CellsOperator(rgsel, false);
            var values = cellOp.GetDoubles();
            Array.Sort(values);
            var min = values[0];
            var max = values[values.Length - 1];
            double raise = Math.Pow(10, precision);
            double down = Math.Pow(10, -precision);
            //count
            double classWidth = Math.Ceiling((max - min) / nbClasses * raise) * down;
            double lower, upper = Math.Floor(min * raise) * down + classWidth;

            var classes = new ContinuousFrequencies.Item[nbClasses];

            classes[0].UpperExcluded = upper;
            var sum = 0d;
			int index = 0; 
            for (int i = 0; i < values.Length; i++) {
                while (!(values[i] < upper)) {
                    lower = upper;
                    index++;
                    classes[index].LowerIncluded = lower;
                    if (index == classes.Length - 1) {
                        upper = double.MaxValue;
                    } else {
                        upper = Math.Round(upper + classWidth, precision);
                        classes[index].UpperExcluded = upper;
                    }
                }
                classes[index].Count += 1;
                sum += values[i];
            }

            var average = sum / values.Length;
            var stdsum = 0d;
            for (int i = 0; i < values.Length; i++)
                stdsum += Math.Pow(values[i] - average, 2);
            var std = Math.Sqrt(stdsum / (values.Length - 1));
			Array.Resize(ref classes, index + 1);
            return new ContinuousFrequencies(classes, values[0], values[values.Length - 1], average, std);
        }

        public class DescreteFrequencies {

            public readonly object[] Values;
            public readonly int[] Frequencies;

            public DescreteFrequencies(object[] items, int[] values) {
                Values = items;
                Frequencies = values;
            }

            public void SortByFrequenceAsc() {
                Array.Sort<int, object>(Frequencies, Values, new ComparerAsc());
            }

            public void SortByFrequenceDesc() {
                Array.Sort<int, object>(Frequencies, Values, new ComparerDesc());
            }

            class ComparerAsc : IComparer<int> {
                public int Compare(int x, int y) {
                    return x - y;
                }
            }

            class ComparerDesc : IComparer<int> {
                public int Compare(int x, int y) {
                    return y - x;
                }
            }
        }

        internal static DescreteFrequencies ComputeDisceteFrequencies(Excel.Range rgsel) {
            var values =  new CellsOperator(rgsel, false).GetValues();
            Array.Sort(values);

            var list = new Dictionary<object, int>();
            int cpt = 1;
            for (int i = 0, j = 1; j < values.Length; i++, j++) {
                if (!object.Equals(values[i], values[j])) {
                    list.Add(values[i], cpt);
                    cpt = 1;
                } else {
                    cpt++;
                }
            }
            list.Add(values[values.Length - 1], cpt);
            var labels = new object[list.Count];
            var counts = new int[list.Count];
            list.Keys.CopyTo(labels, 0);
            list.Values.CopyTo(counts, 0);
            return new DescreteFrequencies(labels, counts);
        }
    }
}
